/*
 * NativeTest.cpp  0.4.0 / Nov 14, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
#include "NativeTest.h"

#ifdef CUTE4J_TEST

#include "QStringListAdapter.h"

// NativeTest :: Public Member Functions. --------------------------------------

/*!
 * \brief Returns the names of test methods.
 *
 * \return the names of test methods
 */
jobjectArray NativeTest::getTestMethods(JNIEnv *env)
{
  Q_ASSERT(env);
  QStringList testMethods;
  const QMetaObject *mo = metaObject();
  for (int i = mo->methodOffset(); i < mo->methodCount(); i++) {
    QMetaMethod method = mo->method(i);
    QString methodName = QString::fromUtf8(method.signature());
    if (methodName.startsWith(QLatin1String("test"))) {
      methodName = methodName.left(methodName.indexOf('('));
      testMethods.append(methodName);
    }
  }
  return QStringListAdapter::toJStringArray(env, testMethods);
}

/*!
 * \brief Invokes the specified method.
 *
 * \param env the JNIEnv pointer
 * \param obj the NativeTest object
 * \param methodName the method name
 *
 * \return \c true if call succeed
 */
bool NativeTest::invokeMethod(JNIEnv *env, jobject obj, jstring methodName)
{
  Q_ASSERT(env);
  Q_ASSERT(obj);
  Q_ASSERT(methodName);
  m_env = env;
  m_obj = env->NewWeakGlobalRef(obj);
  if (env->ExceptionCheck()) { // check for OutOfMemoryError
    env->ExceptionDescribe();
    env->ExceptionClear();
    m_env = 0;
    return false;
  }
  bool succeed = false;
  const char *method = env->GetStringUTFChars(methodName, 0);
  if (method) {
    succeed = QMetaObject::invokeMethod(this, method, Qt::DirectConnection);
    env->ReleaseStringUTFChars(methodName, method);
  }
  m_env->DeleteWeakGlobalRef(m_obj);
  m_env = 0;
  return succeed;
}

/*!
 * \brief Sets up test.
 *
 * \param env the JNIEnv pointer
 */
void NativeTest::setUp(JNIEnv *env)
{
  m_env = env;
  setUp();
  m_env = 0;
}

/*!
 * \brief Tears down test.
 *
 * \param env the JNIEnv pointer
 */
void NativeTest::tearDown(JNIEnv *env)
{
  m_env = env;
  tearDown();
  m_env = 0;
}

// NativeTest :: Public Static Member Functions. -------------------------------

/*!
 * \brief Initializes NativeTest class.
 *
 * \param env the JNIEnv pointer
 * \param cls the NativeTest class
 *
 * \exception FatalError if failed
 */
void NativeTest::initialize(JNIEnv *env, jclass cls)
{
  Q_ASSERT(env);
  Q_ASSERT(cls);
  Q_ASSERT(!sm_failConditionMID);
  if (!INIT_MID(failCondition, cls,
                "(Ljava/lang/String;Ljava/lang/String;I)V")) {
    env->FatalError("NativeTest initialization failed"); // does not return
  }
}

// NativeTest :: Protected Member Functions. -----------------------------------

/*!
 * \brief Sets up test.
 */
void NativeTest::setUp()
{
  // empty method body
}

/*!
 * \brief Tears down test.
 */
void NativeTest::tearDown()
{
  // empty method body
}

/*!
 * \brief Creates the java object.
 *
 * \param className the java class
 *
 * \return created java object
 */
jobject NativeTest::createJObject(const char *className)
{
  return createJObject(className, "()V");
}

/*!
 * \brief Creates the java object.
 *
 * \param className the java class
 * \param ctorSig the constructor signature
 * \param ... the constructor arguments
 *
 * \return created java object
 */
jobject NativeTest::createJObject(const char *className, const char *ctorSig,
                                  ...)
{
  if (m_env) {
    jclass cls = m_env->FindClass(className);
    if (cls) {
      jmethodID constructorMID = m_env->GetMethodID(cls, "<init>", ctorSig);
      if (constructorMID) {
        va_list args;
        va_start(args, ctorSig);
        jobject obj = m_env->NewObject(cls, constructorMID, args);
        va_end(args);
        if (m_env->ExceptionCheck()) {
          m_env->ExceptionDescribe();
          m_env->ExceptionClear();
        }
        return obj;
      }
    }
  }
  return 0;
}

/*!
 * \brief Returns native object pointer.
 *
 * \param obj the java object
 *
 * \return native object pointer
 */
void *NativeTest::nativeObjectPointer(jobject obj)
{
  return nativeObjectPointer(obj, "_bridge");
}

/*!
 * \brief Returns native object pointer.
 *
 * \param obj the java object
 * \param fieldName the field name
 *
 * \return native object pointer
 */
void *NativeTest::nativeObjectPointer(jobject obj, const char *fieldName)
{
  if (m_env && obj && fieldName) {
    jclass cls = m_env->GetObjectClass(obj);
    if (cls) {
      jfieldID pointerFID = m_env->GetFieldID(cls, fieldName, "J");
      if (pointerFID) {
        return reinterpret_cast<void *>(m_env->GetLongField(obj, pointerFID));
      }
    }
  }
  return 0;
}

/*!
 * \brief Fails condition.
 *
 * \param condition the condition
 * \param fileName the file name
 * \param lineNumber the line number
 */
void NativeTest::failCondition(const char *condition, const char *fileName,
                                   int lineNumber)
{
  if (m_env) {
    Q_ASSERT(m_obj);
    jstring jcondition = 0;
    jstring jfileName = 0;
    if (condition) {
      jcondition = JNIWrapper::newStringUTF(m_env, condition);
      Q_ASSERT(jcondition);
    }
    if (fileName) {
      jfileName = JNIWrapper::newStringUTF(m_env, fileName);
      Q_ASSERT(jfileName);
    }
    m_env->CallVoidMethod(m_obj, sm_failConditionMID, jcondition, jfileName,
                          lineNumber);
    // note: do not check for exceptions
  }
}

// NativeTest :: Private Static Data Member. -----------------------------------

jmethodID NativeTest::sm_failConditionMID = 0;

#endif // CUTE4J_TEST
